library(tidyverse) # for graphing and data cleaning
## -- Attaching packages --------------- tidyverse 1.3.0 --
## v ggplot2 3.3.2 v purrr 0.3.4
## v tibble 3.0.3 v dplyr 1.0.2
## v tidyr 1.1.2 v stringr 1.4.0
## v readr 1.3.1 v forcats 0.5.0
## -- Conflicts ------------------ tidyverse_conflicts() --
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(googlesheets4) # for reading googlesheet data
library(lubridate) # for date manipulation
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
library(ggthemes) # for even more plotting themes
library(geofacet) # for special faceting with US map layout
gs4_deauth() # To not have to authorize each time you knit.
theme_set(theme_minimal()) # My favorite ggplot() theme :)
#Lisa's garden data
garden_harvest <- read_sheet("https://docs.google.com/spreadsheets/d/1DekSazCzKqPS2jnGhKue7tLxRU3GVL1oxi-4bEM5IWw/edit?usp=sharing") %>%
mutate(date = ymd(date))
## Reading from "2020_harvest"
## Range "Sheet1"
# Seeds/plants (and other garden supply) costs
supply_costs <- read_sheet("https://docs.google.com/spreadsheets/d/1dPVHwZgR9BxpigbHLnA0U99TtVHHQtUzNB9UR0wvb7o/edit?usp=sharing",
col_types = "ccccnn")
## Reading from "2020_seeds"
## Range "Sheet1"
# Planting dates and locations
plant_date_loc <- read_sheet("https://docs.google.com/spreadsheets/d/11YH0NtXQTncQbUse5wOsTtLSKAiNogjUA21jnX5Pnl4/edit?usp=sharing",
col_types = "cccnDlc")%>%
mutate(date = ymd(date))
## Reading from "seeds_planted"
## Range "Sheet1"
## Warning in .Primitive("as.double")(x, ...): NAs introduced by coercion
# Tidy Tuesday data
kids <- readr::read_csv('https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-09-15/kids.csv')
## Parsed with column specification:
## cols(
## state = col_character(),
## variable = col_character(),
## year = col_double(),
## raw = col_double(),
## inf_adj = col_double(),
## inf_adj_perchild = col_double()
## )
Instructions
Put your name at the top of the document.
For ALL graphs, you should include appropriate labels.
Feel free to change the default theme, which I currently have set to theme_minimal().
Use good coding practice. Read the short sections on good code with pipes and ggplot2. This is part of your grade!
When you are finished with ALL the exercises, uncomment the options at the top so your document looks nicer. Don’t do it before then, or else you might miss some important warnings and messages.
Warm-up exercises with garden data
These exercises will reiterate what you learned in the “Expanding the data wrangling toolkit” tutorial. If you haven’t gone through the tutorial yet, you should do that first.
- Summarize the
garden_harvest data to find the total harvest weight in pounds for each vegetable and day of week. Display the results so that the vegetables are rows but the days of the week are columns.
garden_harvest %>%
mutate(day = wday(date, label = TRUE)) %>%
group_by(vegetable, day) %>%
mutate(wt_lbs = weight*0.00220462) %>%
summarize(daily_wt_lbs = sum(wt_lbs)) %>%
pivot_wider(names_from = day,
values_from = daily_wt_lbs)
## `summarise()` regrouping output by 'vegetable' (override with `.groups` argument)
- Summarize the
garden_harvest data to find the total harvest in pound for each vegetable variety and then try adding the plot variable from the plant_date_loc table. This will not turn out perfectly. What is the problem? How might you fix it?
summarized_garden_harvest <- garden_harvest%>%
group_by(variety, date) %>%
mutate(wt_lbs = weight*0.00220462) %>%
summarize(daily_wt_lbs = sum(wt_lbs))
## `summarise()` regrouping output by 'variety' (override with `.groups` argument)
summarized_garden_harvest %>%
left_join(plant_date_loc,
by = "variety")
I would like to understand how much money I “saved” by gardening, for each vegetable type. Describe how I could use the garden_harvest and supply_cost datasets, along with data from somewhere like this to answer this question. You can answer this in words, referencing various join functions. You don’t need R code but could provide some if it’s helpful.
Subset the data to tomatoes. Reorder the tomato varieties from smallest to largest first harvest date. Create a barplot of total harvest in pounds for each variety, in the new order.
garden_harvest %>%
filter(vegetable %in% c("tomatoes")) %>%
mutate(variety2 = fct_reorder(variety, date, .desc = TRUE)) %>%
group_by(variety2) %>%
summarize(tot_harvest_lbs =
sum(weight*0.00220462),
first_day_harvest = min(date)) %>%
ggplot(aes(x = tot_harvest_lbs, y = variety2)) +
geom_col()
## `summarise()` ungrouping output (override with `.groups` argument)

- In the
garden_harvest data, create two new variables: one that makes the varieties lowercase and another that finds the length of the variety name. Arrange the data by vegetable and length of variety name (smallest to largest), with one row for each vegetable variety. HINT: use str_to_lower(), str_length(), and distinct().
garden_harvest %>%
mutate(variety_length = str_length(variety)) %>%
mutate(variety_lower = str_to_lower(variety)) %>%
arrange(vegetable, variety_length) %>%
distinct(variety, .keep_all = TRUE)
- In the
garden_harvest data, find all distinct vegetable varieties that have “er” or “ar” in their name. HINT: str_detect() with an “or” statement (use the | for “or”) and distinct().
garden_harvest %>%
mutate(variety_er_ar = str_detect(variety, "er|ar")) %>%
distinct(variety, variety_er_ar = TRUE)
Bicycle-Use Patterns
In this activity, you’ll examine some factors that may influence the use of bicycles in a bike-renting program. The data come from Washington, DC and cover the last quarter of 2014.
{300px}
{300px}
Two data tables are available:
Trips contains records of individual rentals
Stations gives the locations of the bike rental stations
Here is the code to read in the data. We do this a little differently than usualy, which is why it is included here rather than at the top of this file. To avoid repeatedly re-reading the files, start the data import chunk with {r cache = TRUE} rather than the usual {r}.
data_site <-
"https://www.macalester.edu/~dshuman1/data/112/2014-Q4-Trips-History-Data.rds"
Trips <- readRDS(gzcon(url(data_site)))
Stations<-read_csv("http://www.macalester.edu/~dshuman1/data/112/DC-Stations.csv")
## Parsed with column specification:
## cols(
## name = col_character(),
## lat = col_double(),
## long = col_double(),
## nbBikes = col_double(),
## nbEmptyDocks = col_double()
## )
NOTE: The Trips data table is a random subset of 10,000 trips from the full quarterly data. Start with this small data table to develop your analysis commands. When you have this working well, you should access the full data set of more than 600,000 events by removing -Small from the name of the data_site.
Temporal patterns
It’s natural to expect that bikes are rented more at some times of day, some days of the week, some months of the year than others. The variable sdate gives the time (including the date) that the rental started. Make the following plots and interpret them:
- A density plot, which is a smoothed out histogram, of the events versus
sdate. Use geom_density().
Trips %>%
ggplot(aes(x = sdate)) +
geom_density()

- A density plot of the events versus time of day. You can use
mutate() with lubridate’s hour() and minute() functions to extract the hour of the day and minute within the hour from sdate. Hint: A minute is 1/60 of an hour, so create a variable where 3:30 is 3.5 and 3:45 is 3.75.
Trips %>%
mutate(time_of_day = round(hour(sdate) + minute(sdate)/60.0, digits = 1)) %>%
ggplot(aes(x = time_of_day)) +
geom_density()

- A bar graph of the events versus day of the week. Put day on the y-axis.
Trips %>%
mutate(day = wday(sdate, label = TRUE)) %>%
ggplot(aes(y = day)) +
geom_bar()

- Facet your graph from exercise 8. by day of the week. Is there a pattern?
Yes, there are less trips being made during the middle of the day from Mon-Fri, while the opposite appears to be true on Sat & Sun. This can likely be explained by people using bikes to get to and from work during the week, and for more lesiurly rides during the weekend.
Trips %>%
mutate(time_of_day = round(hour(sdate) + minute(sdate)/60.0, digits = 1)) %>%
mutate(day = wday(sdate, label = TRUE)) %>%
ggplot(aes(x = time_of_day)) +
geom_density()+
facet_wrap(vars(day))

The variable client describes whether the renter is a regular user (level Registered) or has not joined the bike-rental organization (Causal). The next set of exercises investigate whether these two different categories of users show different rental behavior and how client interacts with the patterns you found in the previous exercises. Repeat the graphic from Exercise @ref(exr:exr-temp) (d) with the following changes:
- Change the graph from exercise 10 to set the
fill aesthetic for geom_density() to the client variable. You should also set alpha = .5 for transparency and color=NA to suppress the outline of the density function.
Trips %>%
mutate(time_of_day = round(hour(sdate) + minute(sdate)/60.0, digits = 1)) %>%
mutate(day = wday(sdate, label = TRUE)) %>%
ggplot(aes(x = time_of_day, fill = client)) +
geom_density(alpha = .5, color = NA)+
facet_wrap(vars(day))

- Change the previous graph by adding the argument
position = position_stack() to geom_density(). In your opinion, is this better or worse in terms of telling a story? What are the advantages/disadvantages of each?
This graph shows the proportions of each type of rider out of the total. Meaning, the area beneath the graph = 1, where the colors stem from how much out of the total each rider is registered as. This graph does a good job in showing how many riders are registered versus not, but does not a good job of showing its usage and distribution across time. The previous graph is better in showing rider usage throughout the day, and I think this is ultimately the better graph in showing who uses the bike rental service and when.
Trips %>%
mutate(time_of_day = round(hour(sdate) + minute(sdate)/60.0, digits = 1)) %>%
mutate(day = wday(sdate, label = TRUE)) %>%
ggplot(aes(x = time_of_day, fill = client)) +
geom_density(alpha = .5, color = NA, position = position_stack())+
facet_wrap(vars(day))

- Add a new variable to the dataset called
weekend which will be “weekend” if the day is Saturday or Sunday and “weekday” otherwise (HINT: use the ifelse() function and the wday() function from lubridate). Then, update the graph from the previous problem by faceting on the new weekend variable.
Trips %>%
mutate(time_of_day = round(hour(sdate) + minute(sdate)/60.0, digits = 1)) %>%
mutate(day = wday(sdate, label = TRUE)) %>%
mutate(weekday = ifelse(day %in% c("Sat", "Sun"), "weekend", "weekday")) %>%
ggplot(aes(x = time_of_day, fill = client)) +
geom_density(alpha = .5, color = NA)+
facet_wrap(vars(weekday))

- Change the graph from the previous problem to facet on
client and fill with weekday. What information does this graph tell you that the previous didn’t? Is one graph better than the other? This graph tells us that most Casual clients use the bike rental service from midday to late evening no matter the day of the week. Meanwhile, the Registered clients use the bike rental service in the mornings and evenings during the week, and during the midday to late evening during the weekends. I don’t necessarily think one graph is better than the other, but is rather more dependent on the question that you are trying to answer (i.e weekend behavior vs. user behavior)
Trips %>%
mutate(time_of_day = round(hour(sdate) + minute(sdate)/60.0, digits = 1)) %>%
mutate(day = wday(sdate, label = TRUE)) %>%
mutate(weekday = ifelse(day %in% c("Sat", "Sun"), "weekend", "weekday")) %>%
ggplot(aes(x = time_of_day, fill = weekday)) +
geom_density(alpha = .5, color = NA)+
facet_wrap(vars(client))

Spatial patterns
- Use the latitude and longitude variables in
Stations to make a visualization of the total number of departures from each station in the Trips data. Use either color or size to show the variation in number of departures. We will improve this plot next week when we learn about maps!
Trips %>%
count(sstation) %>%
rename(name = sstation) %>%
right_join(Stations, by = "name") %>%
ggplot(aes(x = long, y = lat)) +
geom_point()

- Only 14.4% of the trips in our data are carried out by casual users. Create a plot that shows which area(s) have stations with a much higher percentage of departures by casual users. What patterns do you notice? (Again, we’ll improve this next week when we learn about maps). I notice that there are is a higher proportion of Casual riders using services from the middle of the cluster (lat 38.9, long -77.05) to the stations furthest away from this cluster(lat 39.1, long -77.2).
Trips %>%
count(sstation, client) %>%
rename(name = sstation) %>%
right_join(Stations, by = "name") %>%
group_by(name) %>%
mutate(total_prop = n/sum(n)) %>%
pivot_wider(names_from = client,
values_from = total_prop) %>%
select(name, lat, long, Casual) %>%
drop_na() %>%
group_by(name) %>%
ggplot(aes(x = long, y = lat, color = Casual)) +
geom_point()+
scale_color_continuous(high = "#132B43", low = "#56B1F7")

Spatiotemporal patterns
- Make a table with the ten station-date combinations (e.g., 14th & V St., 2014-10-14) with the highest number of departures, sorted from most departures to fewest. Save this to a new dataset and print out the dataset. Hint:
as_date(sdate) converts sdate from date-time format to date format.
Top_Ten_Trips <- Trips %>%
mutate(date = as_date(sdate)) %>%
count(sstation, date) %>%
top_n(10) %>%
arrange(desc(n))
## Selecting by n
Top_Ten_Trips
- Use a join operation to make a table with only those trips whose departures match those top ten station-date combinations from the previous part.
Top_Ten_Trips %>%
left_join(Trips, by = "sstation")
- Build on the code from the previous problem (ie. copy that code below and then %>% into the next step.) and group the trips by client type and day of the week (use the name, not the number). Find the proportion of trips by day within each client type (ie. the proportions for all 7 days within each client type add up to 1). Display your results so day of week is a column and there is a column for each client type. Interpret your results.
During the weekdays it looks like there is a higher proportion of Registered clients using the bike rental service. During the weekends, the opposite is true. This might indicate that Registered clients may use the biking service to get to/from work, while Casual clients may use the bike service for leisure during the weeks.
Top_Ten_Trips %>%
inner_join(Trips, by = c("sstation")) %>%
mutate(days = wday(sdate, label = TRUE)) %>%
group_by(days, client) %>%
summarize(num_clients = n()) %>%
mutate(total_prop = num_clients/sum(num_clients)) %>%
pivot_wider(id_cols = days,
names_from = client,
values_from = total_prop)
## `summarise()` regrouping output by 'days' (override with `.groups` argument)
DID YOU REMEMBER TO GO BACK AND CHANGE THIS SET OF EXERCISES TO THE LARGER DATASET? IF NOT, DO THAT NOW.
Challenge problem!
This problem uses the data from the Tidy Tuesday competition this week, kids. If you need to refresh your memory on the data, read about it here.
- In this exercise, you are going to try to replicate the graph below, created by Georgios Karamanis. I’m sure you can find the exact code on GitHub somewhere, but DON’T DO THAT! You will only be graded for putting an effort into this problem. So, give it a try and see how far you can get without doing too much googling. HINT: use
facet_geo(). The graphic won’t load below since it came from a location on my computer. So, you’ll have to reference the original html on the moodle page to see it.
kids %>%
filter(variable %in% "lib") %>%
ggplot(aes(x = year, y = inf_adj_perchild)) +
geom_line(color = "white", size =2) +
theme(legend.position = "") +
theme_void() +
theme(plot.background = element_rect(fill = "lightsteelblue4")) +
facet_geo(vars(state), grid = "us_state_grid3", label = "name") +
labs(title="Change in public spending on libraries",
subtitle = "Dollars spent per child, adjusted for inflation")+
theme(plot.title = element_text(hjust = 0.5, size =20, face = "bold"),
plot.subtitle = element_text(hjust = 0.5, size = 15))
## Warning: Using `as.character()` on a quosure is deprecated as of rlang 0.3.0.
## Please use `as_label()` or `as_name()` instead.
## This warning is displayed once per session.
## Some values in the specified facet_geo column 'state' do not match the
## 'name' column of the specified grid and will be removed: District of
## Columbia

DID YOU REMEMBER TO UNCOMMENT THE OPTIONS AT THE TOP?
LS0tDQp0aXRsZTogJ1dlZWtseSBFeGVyY2lzZXMgIzMnDQphdXRob3I6ICJBbGlzb24gTGFuZ2UiDQpvdXRwdXQ6IA0KICBodG1sX2RvY3VtZW50Og0KICAgIGtlZXBfbWQ6IFRSVUUNCiAgICB0b2M6IFRSVUUNCiAgICB0b2NfZmxvYXQ6IFRSVUUNCiAgICBkZl9wcmludDogcGFnZWQNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQotLS0NCg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiNrbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIGVycm9yPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UpDQpgYGANCg0KYGBge3IgbGlicmFyaWVzfQ0KbGlicmFyeSh0aWR5dmVyc2UpICAgICAjIGZvciBncmFwaGluZyBhbmQgZGF0YSBjbGVhbmluZw0KbGlicmFyeShnb29nbGVzaGVldHM0KSAjIGZvciByZWFkaW5nIGdvb2dsZXNoZWV0IGRhdGENCmxpYnJhcnkobHVicmlkYXRlKSAgICAgIyBmb3IgZGF0ZSBtYW5pcHVsYXRpb24NCmxpYnJhcnkoZ2d0aGVtZXMpICAgICAgIyBmb3IgZXZlbiBtb3JlIHBsb3R0aW5nIHRoZW1lcw0KbGlicmFyeShnZW9mYWNldCkgICAgICAjIGZvciBzcGVjaWFsIGZhY2V0aW5nIHdpdGggVVMgbWFwIGxheW91dA0KZ3M0X2RlYXV0aCgpICAgICAgICAgICAjIFRvIG5vdCBoYXZlIHRvIGF1dGhvcml6ZSBlYWNoIHRpbWUgeW91IGtuaXQuDQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpKSAgICAgICAjIE15IGZhdm9yaXRlIGdncGxvdCgpIHRoZW1lIDopDQpgYGANCg0KYGBge3IgZGF0YX0NCiNMaXNhJ3MgZ2FyZGVuIGRhdGENCmdhcmRlbl9oYXJ2ZXN0IDwtIHJlYWRfc2hlZXQoImh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzFEZWtTYXpDektxUFMyam5HaEt1ZTd0THhSVTNHVkwxb3hpLTRiRU01SVd3L2VkaXQ/dXNwPXNoYXJpbmciKSAlPiUgDQogIG11dGF0ZShkYXRlID0geW1kKGRhdGUpKQ0KDQojIFNlZWRzL3BsYW50cyAoYW5kIG90aGVyIGdhcmRlbiBzdXBwbHkpIGNvc3RzDQpzdXBwbHlfY29zdHMgPC0gcmVhZF9zaGVldCgiaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMWRQVkh3WmdSOUJ4cGlnYkhMbkEwVTk5VHRWSEhRdFV6TkI5VVIwd3ZiN28vZWRpdD91c3A9c2hhcmluZyIsDQogIGNvbF90eXBlcyA9ICJjY2Njbm4iKQ0KDQojIFBsYW50aW5nIGRhdGVzIGFuZCBsb2NhdGlvbnMNCnBsYW50X2RhdGVfbG9jIDwtIHJlYWRfc2hlZXQoImh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzExWUgwTnRYUVRuY1FiVXNlNXdPc1R0TFNLQWlOb2dqVUEyMWpuWDVQbmw0L2VkaXQ/dXNwPXNoYXJpbmciLA0KICBjb2xfdHlwZXMgPSAiY2NjbkRsYyIpJT4lIA0KICBtdXRhdGUoZGF0ZSA9IHltZChkYXRlKSkNCg0KIyBUaWR5IFR1ZXNkYXkgZGF0YQ0Ka2lkcyA8LSByZWFkcjo6cmVhZF9jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9yZm9yZGF0YXNjaWVuY2UvdGlkeXR1ZXNkYXkvbWFzdGVyL2RhdGEvMjAyMC8yMDIwLTA5LTE1L2tpZHMuY3N2JykNCmBgYA0KDQoNCiMjIEluc3RydWN0aW9ucw0KDQoqIFB1dCB5b3VyIG5hbWUgYXQgdGhlIHRvcCBvZiB0aGUgZG9jdW1lbnQuIA0KDQoqICoqRm9yIEFMTCBncmFwaHMsIHlvdSBzaG91bGQgaW5jbHVkZSBhcHByb3ByaWF0ZSBsYWJlbHMuKiogDQoNCiogRmVlbCBmcmVlIHRvIGNoYW5nZSB0aGUgZGVmYXVsdCB0aGVtZSwgd2hpY2ggSSBjdXJyZW50bHkgaGF2ZSBzZXQgdG8gYHRoZW1lX21pbmltYWwoKWAuIA0KDQoqIFVzZSBnb29kIGNvZGluZyBwcmFjdGljZS4gUmVhZCB0aGUgc2hvcnQgc2VjdGlvbnMgb24gZ29vZCBjb2RlIHdpdGggW3BpcGVzXShodHRwczovL3N0eWxlLnRpZHl2ZXJzZS5vcmcvcGlwZXMuaHRtbCkgYW5kIFtnZ3Bsb3QyXShodHRwczovL3N0eWxlLnRpZHl2ZXJzZS5vcmcvZ2dwbG90Mi5odG1sKS4gKipUaGlzIGlzIHBhcnQgb2YgeW91ciBncmFkZSEqKg0KDQoqIFdoZW4geW91IGFyZSBmaW5pc2hlZCB3aXRoIEFMTCB0aGUgZXhlcmNpc2VzLCB1bmNvbW1lbnQgdGhlIG9wdGlvbnMgYXQgdGhlIHRvcCBzbyB5b3VyIGRvY3VtZW50IGxvb2tzIG5pY2VyLiBEb24ndCBkbyBpdCBiZWZvcmUgdGhlbiwgb3IgZWxzZSB5b3UgbWlnaHQgbWlzcyBzb21lIGltcG9ydGFudCB3YXJuaW5ncyBhbmQgbWVzc2FnZXMuDQoNCg0KIyMgV2FybS11cCBleGVyY2lzZXMgd2l0aCBnYXJkZW4gZGF0YQ0KDQpUaGVzZSBleGVyY2lzZXMgd2lsbCByZWl0ZXJhdGUgd2hhdCB5b3UgbGVhcm5lZCBpbiB0aGUgIkV4cGFuZGluZyB0aGUgZGF0YSB3cmFuZ2xpbmcgdG9vbGtpdCIgdHV0b3JpYWwuIElmIHlvdSBoYXZlbid0IGdvbmUgdGhyb3VnaCB0aGUgdHV0b3JpYWwgeWV0LCB5b3Ugc2hvdWxkIGRvIHRoYXQgZmlyc3QuDQoNCiAgMS4gU3VtbWFyaXplIHRoZSBgZ2FyZGVuX2hhcnZlc3RgIGRhdGEgdG8gZmluZCB0aGUgdG90YWwgaGFydmVzdCB3ZWlnaHQgaW4gcG91bmRzIGZvciBlYWNoIHZlZ2V0YWJsZSBhbmQgZGF5IG9mIHdlZWsuIERpc3BsYXkgdGhlIHJlc3VsdHMgc28gdGhhdCB0aGUgdmVnZXRhYmxlcyBhcmUgcm93cyBidXQgdGhlIGRheXMgb2YgdGhlIHdlZWsgYXJlIGNvbHVtbnMuDQoNCmBgYHtyfQ0KZ2FyZGVuX2hhcnZlc3QgJT4lDQogIG11dGF0ZShkYXkgPSB3ZGF5KGRhdGUsIGxhYmVsID0gVFJVRSkpICU+JSANCiAgZ3JvdXBfYnkodmVnZXRhYmxlLCBkYXkpICU+JSANCiAgbXV0YXRlKHd0X2xicyA9IHdlaWdodCowLjAwMjIwNDYyKSAlPiUNCiAgc3VtbWFyaXplKGRhaWx5X3d0X2xicyA9IHN1bSh3dF9sYnMpKSAlPiUNCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGRheSwNCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBkYWlseV93dF9sYnMpDQpgYGANCg0KICAyLiBTdW1tYXJpemUgdGhlIGBnYXJkZW5faGFydmVzdGAgZGF0YSB0byBmaW5kIHRoZSB0b3RhbCBoYXJ2ZXN0IGluIHBvdW5kIGZvciBlYWNoIHZlZ2V0YWJsZSB2YXJpZXR5IGFuZCB0aGVuIHRyeSBhZGRpbmcgdGhlIGBwbG90YCB2YXJpYWJsZSBmcm9tIHRoZSBgcGxhbnRfZGF0ZV9sb2NgIHRhYmxlLiBUaGlzIHdpbGwgbm90IHR1cm4gb3V0IHBlcmZlY3RseS4gV2hhdCBpcyB0aGUgcHJvYmxlbT8gSG93IG1pZ2h0IHlvdSBmaXggaXQ/DQoNCmBgYHtyfQ0Kc3VtbWFyaXplZF9nYXJkZW5faGFydmVzdCA8LSBnYXJkZW5faGFydmVzdCU+JQ0KICBncm91cF9ieSh2YXJpZXR5LCBkYXRlKSAlPiUNCiAgbXV0YXRlKHd0X2xicyA9IHdlaWdodCowLjAwMjIwNDYyKSAlPiUNCiAgc3VtbWFyaXplKGRhaWx5X3d0X2xicyA9IHN1bSh3dF9sYnMpKQ0KDQpzdW1tYXJpemVkX2dhcmRlbl9oYXJ2ZXN0ICU+JQ0KICBsZWZ0X2pvaW4ocGxhbnRfZGF0ZV9sb2MsDQogICAgICAgICAgICBieSA9ICJ2YXJpZXR5IikNCg0KYGBgDQoNCiAgMy4gSSB3b3VsZCBsaWtlIHRvIHVuZGVyc3RhbmQgaG93IG11Y2ggbW9uZXkgSSAic2F2ZWQiIGJ5IGdhcmRlbmluZywgZm9yIGVhY2ggdmVnZXRhYmxlIHR5cGUuIERlc2NyaWJlIGhvdyBJIGNvdWxkIHVzZSB0aGUgYGdhcmRlbl9oYXJ2ZXN0YCBhbmQgYHN1cHBseV9jb3N0YCBkYXRhc2V0cywgYWxvbmcgd2l0aCBkYXRhIGZyb20gc29tZXdoZXJlIGxpa2UgW3RoaXNdKGh0dHBzOi8vcHJvZHVjdHMud2hvbGVmb29kc21hcmtldC5jb20vc2VhcmNoP3NvcnQ9cmVsZXZhbmNlJnN0b3JlPTEwNTQyKSB0byBhbnN3ZXIgdGhpcyBxdWVzdGlvbi4gWW91IGNhbiBhbnN3ZXIgdGhpcyBpbiB3b3JkcywgcmVmZXJlbmNpbmcgdmFyaW91cyBqb2luIGZ1bmN0aW9ucy4gWW91IGRvbid0IG5lZWQgUiBjb2RlIGJ1dCBjb3VsZCBwcm92aWRlIHNvbWUgaWYgaXQncyBoZWxwZnVsLg0KICANCg0KDQogIDQuIFN1YnNldCB0aGUgZGF0YSB0byB0b21hdG9lcy4gUmVvcmRlciB0aGUgdG9tYXRvIHZhcmlldGllcyBmcm9tIHNtYWxsZXN0IHRvIGxhcmdlc3QgZmlyc3QgaGFydmVzdCBkYXRlLiBDcmVhdGUgYSBiYXJwbG90IG9mIHRvdGFsIGhhcnZlc3QgaW4gcG91bmRzIGZvciBlYWNoIHZhcmlldHksIGluIHRoZSBuZXcgb3JkZXIuDQoNCmBgYHtyfQ0KZ2FyZGVuX2hhcnZlc3QgJT4lDQogIGZpbHRlcih2ZWdldGFibGUgJWluJSBjKCJ0b21hdG9lcyIpKSAlPiUNCiAgbXV0YXRlKHZhcmlldHkyID0gZmN0X3Jlb3JkZXIodmFyaWV0eSwgZGF0ZSwgIC5kZXNjID0gVFJVRSkpICU+JQ0KICBncm91cF9ieSh2YXJpZXR5MikgJT4lIA0KICBzdW1tYXJpemUodG90X2hhcnZlc3RfbGJzID0gDQogICAgICAgICAgICAgIHN1bSh3ZWlnaHQqMC4wMDIyMDQ2MiksIA0KICAgICAgICAgICAgZmlyc3RfZGF5X2hhcnZlc3QgPSBtaW4oZGF0ZSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB0b3RfaGFydmVzdF9sYnMsIHkgPSB2YXJpZXR5MikpICsNCiAgZ2VvbV9jb2woKQ0KICAgDQpgYGANCg0KICA1LiBJbiB0aGUgYGdhcmRlbl9oYXJ2ZXN0YCBkYXRhLCBjcmVhdGUgdHdvIG5ldyB2YXJpYWJsZXM6IG9uZSB0aGF0IG1ha2VzIHRoZSB2YXJpZXRpZXMgbG93ZXJjYXNlIGFuZCBhbm90aGVyIHRoYXQgZmluZHMgdGhlIGxlbmd0aCBvZiB0aGUgdmFyaWV0eSBuYW1lLiBBcnJhbmdlIHRoZSBkYXRhIGJ5IHZlZ2V0YWJsZSBhbmQgbGVuZ3RoIG9mIHZhcmlldHkgbmFtZSAoc21hbGxlc3QgdG8gbGFyZ2VzdCksIHdpdGggb25lIHJvdyBmb3IgZWFjaCB2ZWdldGFibGUgdmFyaWV0eS4gSElOVDogdXNlIGBzdHJfdG9fbG93ZXIoKWAsIGBzdHJfbGVuZ3RoKClgLCBhbmQgYGRpc3RpbmN0KClgLg0KICANCmBgYHtyfQ0KZ2FyZGVuX2hhcnZlc3QgJT4lDQogIG11dGF0ZSh2YXJpZXR5X2xlbmd0aCA9IHN0cl9sZW5ndGgodmFyaWV0eSkpICU+JQ0KICBtdXRhdGUodmFyaWV0eV9sb3dlciA9IHN0cl90b19sb3dlcih2YXJpZXR5KSkgJT4lDQogIGFycmFuZ2UodmVnZXRhYmxlLCB2YXJpZXR5X2xlbmd0aCkgJT4lDQogIGRpc3RpbmN0KHZhcmlldHksIC5rZWVwX2FsbCA9IFRSVUUpDQpgYGANCg0KICA2LiBJbiB0aGUgYGdhcmRlbl9oYXJ2ZXN0YCBkYXRhLCBmaW5kIGFsbCBkaXN0aW5jdCB2ZWdldGFibGUgdmFyaWV0aWVzIHRoYXQgaGF2ZSAiZXIiIG9yICJhciIgaW4gdGhlaXIgbmFtZS4gSElOVDogYHN0cl9kZXRlY3QoKWAgd2l0aCBhbiAib3IiIHN0YXRlbWVudCAodXNlIHRoZSB8IGZvciAib3IiKSBhbmQgYGRpc3RpbmN0KClgLg0KDQpgYGB7cn0NCmdhcmRlbl9oYXJ2ZXN0ICU+JQ0KICBtdXRhdGUodmFyaWV0eV9lcl9hciA9IHN0cl9kZXRlY3QodmFyaWV0eSwgImVyfGFyIikpICU+JQ0KICBkaXN0aW5jdCh2YXJpZXR5LCB2YXJpZXR5X2VyX2FyID0gVFJVRSkNCmBgYA0KDQoNCiMjIEJpY3ljbGUtVXNlIFBhdHRlcm5zDQoNCkluIHRoaXMgYWN0aXZpdHksIHlvdSdsbCBleGFtaW5lIHNvbWUgZmFjdG9ycyB0aGF0IG1heSBpbmZsdWVuY2UgdGhlIHVzZSBvZiBiaWN5Y2xlcyBpbiBhIGJpa2UtcmVudGluZyBwcm9ncmFtLiAgVGhlIGRhdGEgY29tZSBmcm9tIFdhc2hpbmd0b24sIERDIGFuZCBjb3ZlciB0aGUgbGFzdCBxdWFydGVyIG9mIDIwMTQuDQoNCjxjZW50ZXI+DQoNCiFbQSB0eXBpY2FsIENhcGl0YWwgQmlrZXNoYXJlIHN0YXRpb24uIFRoaXMgb25lIGlzIGF0IEZsb3JpZGEgYW5kIENhbGlmb3JuaWEsIG5leHQgdG8gUGxlYXNhbnQgUG9wcy5dKGh0dHBzOi8vd3d3Lm1hY2FsZXN0ZXIuZWR1L35kc2h1bWFuMS9kYXRhLzExMi9iaWtlX3N0YXRpb24uanBnKXszMDBweH0NCg0KDQohW09uZSBvZiB0aGUgdmFucyB1c2VkIHRvIHJlZGlzdHJpYnV0ZSBiaWN5Y2xlcyB0byBkaWZmZXJlbnQgc3RhdGlvbnMuXShodHRwczovL3d3dy5tYWNhbGVzdGVyLmVkdS9+ZHNodW1hbjEvZGF0YS8xMTIvYmlrZV92YW4uanBnKXszMDBweH0NCg0KPC9jZW50ZXI+DQoNClR3byBkYXRhIHRhYmxlcyBhcmUgYXZhaWxhYmxlOg0KDQotIGBUcmlwc2AgY29udGFpbnMgcmVjb3JkcyBvZiBpbmRpdmlkdWFsIHJlbnRhbHMNCi0gYFN0YXRpb25zYCBnaXZlcyB0aGUgbG9jYXRpb25zIG9mIHRoZSBiaWtlIHJlbnRhbCBzdGF0aW9ucw0KDQpIZXJlIGlzIHRoZSBjb2RlIHRvIHJlYWQgaW4gdGhlIGRhdGEuIFdlIGRvIHRoaXMgYSBsaXR0bGUgZGlmZmVyZW50bHkgdGhhbiB1c3VhbHksIHdoaWNoIGlzIHdoeSBpdCBpcyBpbmNsdWRlZCBoZXJlIHJhdGhlciB0aGFuIGF0IHRoZSB0b3Agb2YgdGhpcyBmaWxlLiBUbyBhdm9pZCByZXBlYXRlZGx5IHJlLXJlYWRpbmcgdGhlIGZpbGVzLCBzdGFydCB0aGUgZGF0YSBpbXBvcnQgY2h1bmsgd2l0aCBge3IgY2FjaGUgPSBUUlVFfWAgcmF0aGVyIHRoYW4gdGhlIHVzdWFsIGB7cn1gLg0KDQpgYGB7ciBjYWNoZT1UUlVFfQ0KZGF0YV9zaXRlIDwtIA0KICAiaHR0cHM6Ly93d3cubWFjYWxlc3Rlci5lZHUvfmRzaHVtYW4xL2RhdGEvMTEyLzIwMTQtUTQtVHJpcHMtSGlzdG9yeS1EYXRhLnJkcyIgDQpUcmlwcyA8LSByZWFkUkRTKGd6Y29uKHVybChkYXRhX3NpdGUpKSkNClN0YXRpb25zPC1yZWFkX2NzdigiaHR0cDovL3d3dy5tYWNhbGVzdGVyLmVkdS9+ZHNodW1hbjEvZGF0YS8xMTIvREMtU3RhdGlvbnMuY3N2IikNCmBgYA0KDQoqKk5PVEU6KiogVGhlIGBUcmlwc2AgZGF0YSB0YWJsZSBpcyBhIHJhbmRvbSBzdWJzZXQgb2YgMTAsMDAwIHRyaXBzIGZyb20gdGhlIGZ1bGwgcXVhcnRlcmx5IGRhdGEuIFN0YXJ0IHdpdGggdGhpcyBzbWFsbCBkYXRhIHRhYmxlIHRvIGRldmVsb3AgeW91ciBhbmFseXNpcyBjb21tYW5kcy4gKipXaGVuIHlvdSBoYXZlIHRoaXMgd29ya2luZyB3ZWxsLCB5b3Ugc2hvdWxkIGFjY2VzcyB0aGUgZnVsbCBkYXRhIHNldCBvZiBtb3JlIHRoYW4gNjAwLDAwMCBldmVudHMgYnkgcmVtb3ZpbmcgYC1TbWFsbGAgZnJvbSB0aGUgbmFtZSBvZiB0aGUgYGRhdGFfc2l0ZWAuKioNCg0KIyMjIFRlbXBvcmFsIHBhdHRlcm5zDQoNCkl0J3MgbmF0dXJhbCB0byBleHBlY3QgdGhhdCBiaWtlcyBhcmUgcmVudGVkIG1vcmUgYXQgc29tZSB0aW1lcyBvZiBkYXksIHNvbWUgZGF5cyBvZiB0aGUgd2Vlaywgc29tZSBtb250aHMgb2YgdGhlIHllYXIgdGhhbiBvdGhlcnMuIFRoZSB2YXJpYWJsZSBgc2RhdGVgIGdpdmVzIHRoZSB0aW1lIChpbmNsdWRpbmcgdGhlIGRhdGUpIHRoYXQgdGhlIHJlbnRhbCBzdGFydGVkLiBNYWtlIHRoZSBmb2xsb3dpbmcgcGxvdHMgYW5kIGludGVycHJldCB0aGVtOg0KDQogIDcuIEEgZGVuc2l0eSBwbG90LCB3aGljaCBpcyBhIHNtb290aGVkIG91dCBoaXN0b2dyYW0sIG9mIHRoZSBldmVudHMgdmVyc3VzIGBzZGF0ZWAuIFVzZSBgZ2VvbV9kZW5zaXR5KClgLg0KICANCmBgYHtyfQ0KVHJpcHMgJT4lDQogIGdncGxvdChhZXMoeCA9IHNkYXRlKSkgKw0KICBnZW9tX2RlbnNpdHkoKQ0KYGBgDQogIA0KICA4LiBBIGRlbnNpdHkgcGxvdCBvZiB0aGUgZXZlbnRzIHZlcnN1cyB0aW1lIG9mIGRheS4gIFlvdSBjYW4gdXNlIGBtdXRhdGUoKWAgd2l0aCBgbHVicmlkYXRlYCdzICBgaG91cigpYCBhbmQgYG1pbnV0ZSgpYCBmdW5jdGlvbnMgdG8gZXh0cmFjdCB0aGUgaG91ciBvZiB0aGUgZGF5IGFuZCBtaW51dGUgd2l0aGluIHRoZSBob3VyIGZyb20gYHNkYXRlYC4gSGludDogQSBtaW51dGUgaXMgMS82MCBvZiBhbiBob3VyLCBzbyBjcmVhdGUgYSB2YXJpYWJsZSB3aGVyZSAzOjMwIGlzIDMuNSBhbmQgMzo0NSBpcyAzLjc1Lg0KICANCmBgYHtyfQ0KVHJpcHMgJT4lDQogIG11dGF0ZSh0aW1lX29mX2RheSA9IHJvdW5kKGhvdXIoc2RhdGUpICsgbWludXRlKHNkYXRlKS82MC4wLCBkaWdpdHMgPSAxKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IHRpbWVfb2ZfZGF5KSkgKw0KICBnZW9tX2RlbnNpdHkoKQ0KDQpgYGANCiAgDQogIDkuIEEgYmFyIGdyYXBoIG9mIHRoZSBldmVudHMgdmVyc3VzIGRheSBvZiB0aGUgd2Vlay4gUHV0IGRheSBvbiB0aGUgeS1heGlzLg0KICANCmBgYHtyfQ0KVHJpcHMgJT4lDQogIG11dGF0ZShkYXkgPSB3ZGF5KHNkYXRlLCBsYWJlbCA9IFRSVUUpKSAlPiUNCiAgZ2dwbG90KGFlcyh5ID0gZGF5KSkgKw0KICBnZW9tX2JhcigpDQogIA0KYGBgDQogIA0KICAxMC4gRmFjZXQgeW91ciBncmFwaCBmcm9tIGV4ZXJjaXNlIDguIGJ5IGRheSBvZiB0aGUgd2Vlay4gSXMgdGhlcmUgYSBwYXR0ZXJuPw0KICANClllcywgdGhlcmUgYXJlIGxlc3MgdHJpcHMgYmVpbmcgbWFkZSBkdXJpbmcgdGhlIG1pZGRsZSBvZiB0aGUgZGF5IGZyb20gTW9uLUZyaSwgd2hpbGUgdGhlIG9wcG9zaXRlIGFwcGVhcnMgdG8gYmUgdHJ1ZSBvbiBTYXQgJiBTdW4uIFRoaXMgY2FuIGxpa2VseSBiZSBleHBsYWluZWQgYnkgcGVvcGxlIHVzaW5nIGJpa2VzIHRvIGdldCB0byBhbmQgZnJvbSB3b3JrIGR1cmluZyB0aGUgd2VlaywgYW5kIGZvciBtb3JlIGxlc2l1cmx5IHJpZGVzIGR1cmluZyB0aGUgd2Vla2VuZC4NCmBgYHtyfQ0KVHJpcHMgJT4lDQogIG11dGF0ZSh0aW1lX29mX2RheSA9IHJvdW5kKGhvdXIoc2RhdGUpICsgbWludXRlKHNkYXRlKS82MC4wLCBkaWdpdHMgPSAxKSkgJT4lDQogIG11dGF0ZShkYXkgPSB3ZGF5KHNkYXRlLCBsYWJlbCA9IFRSVUUpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gdGltZV9vZl9kYXkpKSArDQogIGdlb21fZGVuc2l0eSgpKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSkNCmBgYA0KICANClRoZSB2YXJpYWJsZSBgY2xpZW50YCBkZXNjcmliZXMgd2hldGhlciB0aGUgcmVudGVyIGlzIGEgcmVndWxhciB1c2VyIChsZXZlbCBgUmVnaXN0ZXJlZGApIG9yIGhhcyBub3Qgam9pbmVkIHRoZSBiaWtlLXJlbnRhbCBvcmdhbml6YXRpb24gKGBDYXVzYWxgKS4gVGhlIG5leHQgc2V0IG9mIGV4ZXJjaXNlcyBpbnZlc3RpZ2F0ZSB3aGV0aGVyIHRoZXNlIHR3byBkaWZmZXJlbnQgY2F0ZWdvcmllcyBvZiB1c2VycyBzaG93IGRpZmZlcmVudCByZW50YWwgYmVoYXZpb3IgYW5kIGhvdyBgY2xpZW50YCBpbnRlcmFjdHMgd2l0aCB0aGUgcGF0dGVybnMgeW91IGZvdW5kIGluIHRoZSBwcmV2aW91cyBleGVyY2lzZXMuIFJlcGVhdCB0aGUgZ3JhcGhpYyBmcm9tIEV4ZXJjaXNlIFxAcmVmKGV4cjpleHItdGVtcCkgKGQpIHdpdGggdGhlIGZvbGxvd2luZyBjaGFuZ2VzOg0KDQogIDExLiBDaGFuZ2UgdGhlIGdyYXBoIGZyb20gZXhlcmNpc2UgMTAgdG8gc2V0IHRoZSBgZmlsbGAgYWVzdGhldGljIGZvciBgZ2VvbV9kZW5zaXR5KClgIHRvIHRoZSBgY2xpZW50YCB2YXJpYWJsZS4gWW91IHNob3VsZCBhbHNvIHNldCBgYWxwaGEgPSAuNWAgZm9yIHRyYW5zcGFyZW5jeSBhbmQgYGNvbG9yPU5BYCB0byBzdXBwcmVzcyB0aGUgb3V0bGluZSBvZiB0aGUgZGVuc2l0eSBmdW5jdGlvbi4NCiAgDQpgYGB7cn0NClRyaXBzICU+JQ0KICBtdXRhdGUodGltZV9vZl9kYXkgPSByb3VuZChob3VyKHNkYXRlKSArIG1pbnV0ZShzZGF0ZSkvNjAuMCwgZGlnaXRzID0gMSkpICU+JQ0KICBtdXRhdGUoZGF5ID0gd2RheShzZGF0ZSwgbGFiZWwgPSBUUlVFKSkgJT4lDQogIGdncGxvdChhZXMoeCA9IHRpbWVfb2ZfZGF5LCBmaWxsID0gY2xpZW50KSkgKw0KICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAuNSwgY29sb3IgPSBOQSkrDQogIGZhY2V0X3dyYXAodmFycyhkYXkpKQ0KYGBgDQoNCiAgMTIuIENoYW5nZSB0aGUgcHJldmlvdXMgZ3JhcGggYnkgYWRkaW5nIHRoZSBhcmd1bWVudCBgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjaygpYCB0byBgZ2VvbV9kZW5zaXR5KClgLiBJbiB5b3VyIG9waW5pb24sIGlzIHRoaXMgYmV0dGVyIG9yIHdvcnNlIGluIHRlcm1zIG9mIHRlbGxpbmcgYSBzdG9yeT8gV2hhdCBhcmUgdGhlIGFkdmFudGFnZXMvZGlzYWR2YW50YWdlcyBvZiBlYWNoPw0KDQogIFRoaXMgZ3JhcGggc2hvd3MgdGhlIHByb3BvcnRpb25zIG9mIGVhY2ggdHlwZSBvZiByaWRlciBvdXQgb2YgdGhlIHRvdGFsLiBNZWFuaW5nLCB0aGUgYXJlYSBiZW5lYXRoIHRoZSBncmFwaCA9IDEsIHdoZXJlIHRoZSBjb2xvcnMgc3RlbSBmcm9tIGhvdyBtdWNoIG91dCBvZiB0aGUgdG90YWwgZWFjaCByaWRlciBpcyByZWdpc3RlcmVkIGFzLiBUaGlzIGdyYXBoIGRvZXMgYSBnb29kIGpvYiBpbiBzaG93aW5nIGhvdyBtYW55IHJpZGVycyBhcmUgcmVnaXN0ZXJlZCB2ZXJzdXMgbm90LCBidXQgZG9lcyBub3QgYSBnb29kIGpvYiBvZiBzaG93aW5nIGl0cyB1c2FnZSBhbmQgZGlzdHJpYnV0aW9uIGFjcm9zcyB0aW1lLiBUaGUgcHJldmlvdXMgZ3JhcGggaXMgYmV0dGVyIGluIHNob3dpbmcgcmlkZXIgdXNhZ2UgdGhyb3VnaG91dCB0aGUgZGF5LCBhbmQgSSB0aGluayB0aGlzIGlzIHVsdGltYXRlbHkgdGhlIGJldHRlciBncmFwaCBpbiBzaG93aW5nIHdobyB1c2VzIHRoZSBiaWtlIHJlbnRhbCBzZXJ2aWNlIGFuZCB3aGVuLg0KYGBge3J9DQpUcmlwcyAlPiUNCiAgbXV0YXRlKHRpbWVfb2ZfZGF5ID0gcm91bmQoaG91cihzZGF0ZSkgKyBtaW51dGUoc2RhdGUpLzYwLjAsIGRpZ2l0cyA9IDEpKSAlPiUNCiAgbXV0YXRlKGRheSA9IHdkYXkoc2RhdGUsIGxhYmVsID0gVFJVRSkpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX29mX2RheSwgZmlsbCA9IGNsaWVudCkpICsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gLjUsIGNvbG9yID0gTkEsIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2soKSkrDQogIGZhY2V0X3dyYXAodmFycyhkYXkpKQ0KYGBgDQogIA0KICAxMy4gQWRkIGEgbmV3IHZhcmlhYmxlIHRvIHRoZSBkYXRhc2V0IGNhbGxlZCBgd2Vla2VuZGAgd2hpY2ggd2lsbCBiZSAid2Vla2VuZCIgaWYgdGhlIGRheSBpcyBTYXR1cmRheSBvciBTdW5kYXkgYW5kICAid2Vla2RheSIgb3RoZXJ3aXNlIChISU5UOiB1c2UgdGhlIGBpZmVsc2UoKWAgZnVuY3Rpb24gYW5kIHRoZSBgd2RheSgpYCBmdW5jdGlvbiBmcm9tIGBsdWJyaWRhdGVgKS4gVGhlbiwgdXBkYXRlIHRoZSBncmFwaCBmcm9tIHRoZSBwcmV2aW91cyBwcm9ibGVtIGJ5IGZhY2V0aW5nIG9uIHRoZSBuZXcgYHdlZWtlbmRgIHZhcmlhYmxlLiANCiAgDQpgYGB7ciBmaWcud2lkdGg9IDgsIGZpZy5oZWlnaHQ9Nn0NClRyaXBzICU+JQ0KICBtdXRhdGUodGltZV9vZl9kYXkgPSByb3VuZChob3VyKHNkYXRlKSArIG1pbnV0ZShzZGF0ZSkvNjAuMCwgZGlnaXRzID0gMSkpICU+JQ0KICBtdXRhdGUoZGF5ID0gd2RheShzZGF0ZSwgbGFiZWwgPSBUUlVFKSkgJT4lDQogIG11dGF0ZSh3ZWVrZGF5ID0gaWZlbHNlKGRheSAlaW4lIGMoIlNhdCIsICJTdW4iKSwgIndlZWtlbmQiLCAid2Vla2RheSIpKSAgJT4lDQogICBnZ3Bsb3QoYWVzKHggPSB0aW1lX29mX2RheSwgZmlsbCA9IGNsaWVudCkpICsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gLjUsIGNvbG9yID0gTkEpKw0KICBmYWNldF93cmFwKHZhcnMod2Vla2RheSkpDQoNCmBgYA0KICANCiAgMTQuIENoYW5nZSB0aGUgZ3JhcGggZnJvbSB0aGUgcHJldmlvdXMgcHJvYmxlbSB0byBmYWNldCBvbiBgY2xpZW50YCBhbmQgZmlsbCB3aXRoIGB3ZWVrZGF5YC4gV2hhdCBpbmZvcm1hdGlvbiBkb2VzIHRoaXMgZ3JhcGggdGVsbCB5b3UgdGhhdCB0aGUgcHJldmlvdXMgZGlkbid0PyBJcyBvbmUgZ3JhcGggYmV0dGVyIHRoYW4gdGhlIG90aGVyPw0KICBUaGlzIGdyYXBoIHRlbGxzIHVzIHRoYXQgbW9zdCBDYXN1YWwgY2xpZW50cyB1c2UgdGhlIGJpa2UgcmVudGFsIHNlcnZpY2UgZnJvbSBtaWRkYXkgdG8gbGF0ZSBldmVuaW5nIG5vIG1hdHRlciB0aGUgZGF5IG9mIHRoZSB3ZWVrLiBNZWFud2hpbGUsIHRoZSBSZWdpc3RlcmVkIGNsaWVudHMgdXNlIHRoZSBiaWtlIHJlbnRhbCBzZXJ2aWNlIGluIHRoZSBtb3JuaW5ncyBhbmQgZXZlbmluZ3MgZHVyaW5nIHRoZSB3ZWVrLCBhbmQgZHVyaW5nIHRoZSBtaWRkYXkgdG8gbGF0ZSBldmVuaW5nIGR1cmluZyB0aGUgd2Vla2VuZHMuIEkgZG9uJ3QgbmVjZXNzYXJpbHkgdGhpbmsgb25lIGdyYXBoIGlzIGJldHRlciB0aGFuIHRoZSBvdGhlciwgYnV0IGlzIHJhdGhlciBtb3JlIGRlcGVuZGVudCBvbiB0aGUgcXVlc3Rpb24gdGhhdCB5b3UgYXJlIHRyeWluZyB0byBhbnN3ZXIgKGkuZSB3ZWVrZW5kIGJlaGF2aW9yIHZzLiB1c2VyIGJlaGF2aW9yKQ0KICANCmBgYHtyIGZpZy53aWR0aD0gOCwgZmlnLmhlaWdodD02fQ0KVHJpcHMgJT4lDQogIG11dGF0ZSh0aW1lX29mX2RheSA9IHJvdW5kKGhvdXIoc2RhdGUpICsgbWludXRlKHNkYXRlKS82MC4wLCBkaWdpdHMgPSAxKSkgJT4lDQogIG11dGF0ZShkYXkgPSB3ZGF5KHNkYXRlLCBsYWJlbCA9IFRSVUUpKSAlPiUNCiAgbXV0YXRlKHdlZWtkYXkgPSBpZmVsc2UoZGF5ICVpbiUgYygiU2F0IiwgIlN1biIpLCAid2Vla2VuZCIsICJ3ZWVrZGF5IikpICAlPiUNCiAgIGdncGxvdChhZXMoeCA9IHRpbWVfb2ZfZGF5LCBmaWxsID0gd2Vla2RheSkpICsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gLjUsIGNvbG9yID0gTkEpKw0KICBmYWNldF93cmFwKHZhcnMoY2xpZW50KSkNCmBgYA0KICANCiMjIyBTcGF0aWFsIHBhdHRlcm5zDQoNCiAgMTUuIFVzZSB0aGUgbGF0aXR1ZGUgYW5kIGxvbmdpdHVkZSB2YXJpYWJsZXMgaW4gYFN0YXRpb25zYCB0byBtYWtlIGEgdmlzdWFsaXphdGlvbiBvZiB0aGUgdG90YWwgbnVtYmVyIG9mIGRlcGFydHVyZXMgZnJvbSBlYWNoIHN0YXRpb24gaW4gdGhlIGBUcmlwc2AgZGF0YS4gVXNlIGVpdGhlciBjb2xvciBvciBzaXplIHRvIHNob3cgdGhlIHZhcmlhdGlvbiBpbiBudW1iZXIgb2YgZGVwYXJ0dXJlcy4gV2Ugd2lsbCBpbXByb3ZlIHRoaXMgcGxvdCBuZXh0IHdlZWsgd2hlbiB3ZSBsZWFybiBhYm91dCBtYXBzIQ0KICANCmBgYHtyIGZpZy5oZWlnaHQ9IDV9DQpUcmlwcyAlPiUNCiAgY291bnQoc3N0YXRpb24pICU+JQ0KICByZW5hbWUobmFtZSA9IHNzdGF0aW9uKSAlPiUNCiAgcmlnaHRfam9pbihTdGF0aW9ucywgYnkgPSAibmFtZSIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBsb25nLCB5ID0gbGF0KSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KICANCiAgMTYuIE9ubHkgMTQuNCUgb2YgdGhlIHRyaXBzIGluIG91ciBkYXRhIGFyZSBjYXJyaWVkIG91dCBieSBjYXN1YWwgdXNlcnMuIENyZWF0ZSBhIHBsb3QgdGhhdCBzaG93cyB3aGljaCBhcmVhKHMpIGhhdmUgc3RhdGlvbnMgd2l0aCBhIG11Y2ggaGlnaGVyIHBlcmNlbnRhZ2Ugb2YgZGVwYXJ0dXJlcyBieSBjYXN1YWwgdXNlcnMuIFdoYXQgcGF0dGVybnMgZG8geW91IG5vdGljZT8gKEFnYWluLCB3ZSdsbCBpbXByb3ZlIHRoaXMgbmV4dCB3ZWVrIHdoZW4gd2UgbGVhcm4gYWJvdXQgbWFwcykuDQogIEkgbm90aWNlIHRoYXQgdGhlcmUgYXJlIGlzIGEgaGlnaGVyIHByb3BvcnRpb24gb2YgQ2FzdWFsIHJpZGVycyB1c2luZyBzZXJ2aWNlcyBmcm9tIHRoZSBtaWRkbGUgb2YgdGhlIGNsdXN0ZXIgKGxhdCAzOC45LCBsb25nIC03Ny4wNSkgdG8gdGhlIHN0YXRpb25zIGZ1cnRoZXN0IGF3YXkgZnJvbSB0aGlzIGNsdXN0ZXIobGF0IDM5LjEsIGxvbmcgLTc3LjIpLg0KICANCmBgYHtyIGZpZy5oZWlnaHQ9IDV9DQpUcmlwcyAlPiUNCiAgY291bnQoc3N0YXRpb24sIGNsaWVudCkgJT4lDQogIHJlbmFtZShuYW1lID0gc3N0YXRpb24pICU+JQ0KICByaWdodF9qb2luKFN0YXRpb25zLCBieSA9ICJuYW1lIikgJT4lDQogIGdyb3VwX2J5KG5hbWUpICU+JQ0KICBtdXRhdGUodG90YWxfcHJvcCA9IG4vc3VtKG4pKSAlPiUgDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBjbGllbnQsDQogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gdG90YWxfcHJvcCkgJT4lDQogIHNlbGVjdChuYW1lLCBsYXQsIGxvbmcsIENhc3VhbCkgICU+JQ0KICBkcm9wX25hKCkgJT4lDQogIGdyb3VwX2J5KG5hbWUpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBsb25nLCB5ID0gbGF0LCBjb2xvciA9IENhc3VhbCkpICsNCiAgZ2VvbV9wb2ludCgpKw0KICBzY2FsZV9jb2xvcl9jb250aW51b3VzKGhpZ2ggPSAiIzEzMkI0MyIsIGxvdyA9ICIjNTZCMUY3IikNCiANCmBgYA0KICANCiMjIyBTcGF0aW90ZW1wb3JhbCBwYXR0ZXJucw0KDQogIDE3LiBNYWtlIGEgdGFibGUgd2l0aCB0aGUgdGVuIHN0YXRpb24tZGF0ZSBjb21iaW5hdGlvbnMgKGUuZy4sIDE0dGggJiBWIFN0LiwgMjAxNC0xMC0xNCkgd2l0aCB0aGUgaGlnaGVzdCBudW1iZXIgb2YgZGVwYXJ0dXJlcywgc29ydGVkIGZyb20gbW9zdCBkZXBhcnR1cmVzIHRvIGZld2VzdC4gU2F2ZSB0aGlzIHRvIGEgbmV3IGRhdGFzZXQgYW5kIHByaW50IG91dCB0aGUgZGF0YXNldC4gSGludDogYGFzX2RhdGUoc2RhdGUpYCBjb252ZXJ0cyBgc2RhdGVgIGZyb20gZGF0ZS10aW1lIGZvcm1hdCB0byBkYXRlIGZvcm1hdC4gDQogIA0KYGBge3J9DQpUb3BfVGVuX1RyaXBzIDwtIFRyaXBzICU+JQ0KICBtdXRhdGUoZGF0ZSA9IGFzX2RhdGUoc2RhdGUpKSAlPiUNCiAgY291bnQoc3N0YXRpb24sIGRhdGUpICU+JQ0KICB0b3BfbigxMCkgJT4lDQogIGFycmFuZ2UoZGVzYyhuKSkNCiAgVG9wX1Rlbl9Ucmlwcw0KICANCmBgYA0KICANCiAgMTguIFVzZSBhIGpvaW4gb3BlcmF0aW9uIHRvIG1ha2UgYSB0YWJsZSB3aXRoIG9ubHkgdGhvc2UgdHJpcHMgd2hvc2UgZGVwYXJ0dXJlcyBtYXRjaCB0aG9zZSB0b3AgdGVuIHN0YXRpb24tZGF0ZSBjb21iaW5hdGlvbnMgZnJvbSB0aGUgcHJldmlvdXMgcGFydC4NCiAgDQpgYGB7cn0NClRvcF9UZW5fVHJpcHMgJT4lDQogIGxlZnRfam9pbihUcmlwcywgYnkgPSAic3N0YXRpb24iKSANCg0KYGBgDQogIA0KICAxOS4gQnVpbGQgb24gdGhlIGNvZGUgZnJvbSB0aGUgcHJldmlvdXMgcHJvYmxlbSAoaWUuIGNvcHkgdGhhdCBjb2RlIGJlbG93IGFuZCB0aGVuICU+JSBpbnRvIHRoZSBuZXh0IHN0ZXAuKSBhbmQgZ3JvdXAgdGhlIHRyaXBzIGJ5IGNsaWVudCB0eXBlIGFuZCBkYXkgb2YgdGhlIHdlZWsgKHVzZSB0aGUgbmFtZSwgbm90IHRoZSBudW1iZXIpLiBGaW5kIHRoZSBwcm9wb3J0aW9uIG9mIHRyaXBzIGJ5IGRheSB3aXRoaW4gZWFjaCBjbGllbnQgdHlwZSAoaWUuIHRoZSBwcm9wb3J0aW9ucyBmb3IgYWxsIDcgZGF5cyB3aXRoaW4gZWFjaCBjbGllbnQgdHlwZSBhZGQgdXAgdG8gMSkuIERpc3BsYXkgeW91ciByZXN1bHRzIHNvIGRheSBvZiB3ZWVrIGlzIGEgY29sdW1uIGFuZCB0aGVyZSBpcyBhIGNvbHVtbiBmb3IgZWFjaCBjbGllbnQgdHlwZS4gSW50ZXJwcmV0IHlvdXIgcmVzdWx0cy4NCiAgDQogIER1cmluZyB0aGUgd2Vla2RheXMgaXQgbG9va3MgbGlrZSB0aGVyZSBpcyBhIGhpZ2hlciBwcm9wb3J0aW9uIG9mIFJlZ2lzdGVyZWQgY2xpZW50cyB1c2luZyB0aGUgYmlrZSByZW50YWwgc2VydmljZS4gRHVyaW5nIHRoZSB3ZWVrZW5kcywgdGhlIG9wcG9zaXRlIGlzIHRydWUuIFRoaXMgbWlnaHQgaW5kaWNhdGUgdGhhdCBSZWdpc3RlcmVkIGNsaWVudHMgbWF5IHVzZSB0aGUgYmlraW5nIHNlcnZpY2UgdG8gZ2V0IHRvL2Zyb20gd29yaywgd2hpbGUgQ2FzdWFsIGNsaWVudHMgbWF5IHVzZSB0aGUgYmlrZSBzZXJ2aWNlIGZvciBsZWlzdXJlIGR1cmluZyB0aGUgd2Vla3MuDQoNCmBgYHtyfQ0KVG9wX1Rlbl9UcmlwcyAlPiUNCiAgaW5uZXJfam9pbihUcmlwcywgYnkgPSBjKCJzc3RhdGlvbiIpKSAlPiUNCiAgbXV0YXRlKGRheXMgPSB3ZGF5KHNkYXRlLCBsYWJlbCA9IFRSVUUpKSAlPiUNCiAgZ3JvdXBfYnkoZGF5cywgY2xpZW50KSAlPiUNCiAgc3VtbWFyaXplKG51bV9jbGllbnRzID0gbigpKSAlPiUNCiAgbXV0YXRlKHRvdGFsX3Byb3AgPSBudW1fY2xpZW50cy9zdW0obnVtX2NsaWVudHMpKSAlPiUNCiAgcGl2b3Rfd2lkZXIoaWRfY29scyA9IGRheXMsDQogICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBjbGllbnQsDQogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gdG90YWxfcHJvcCkNCmBgYA0KDQoqKkRJRCBZT1UgUkVNRU1CRVIgVE8gR08gQkFDSyBBTkQgQ0hBTkdFIFRISVMgU0VUIE9GIEVYRVJDSVNFUyBUTyBUSEUgTEFSR0VSIERBVEFTRVQ/IElGIE5PVCwgRE8gVEhBVCBOT1cuKioNCg0KIyMgR2l0SHViIGxpbmsNCg0KICAyMC4gQmVsb3csIHByb3ZpZGUgYSBsaW5rIHRvIHlvdXIgR2l0SHViIHBhZ2Ugd2l0aCB0aGlzIHNldCBvZiBXZWVrbHkgRXhlcmNpc2VzLiBTcGVjaWZpY2FsbHksIGlmIHRoZSBuYW1lIG9mIHRoZSBmaWxlIGlzIDAzX2V4ZXJjaXNlcy5SbWQsIHByb3ZpZGUgYSBsaW5rIHRvIHRoZSAwM19leGVyY2lzZXMubWQgZmlsZSwgd2hpY2ggaXMgdGhlIG9uZSB0aGF0IHdpbGwgYmUgbW9zdCByZWFkYWJsZSBvbiBHaXRIdWIuDQoNCihodHRwczovL2dpdGh1Yi5jb20vYWxpc29ubGFuZ2U0NS9TVEFUMTEyXzAzX2V4ZXJjaXNlcy9ibG9iL21hc3Rlci8wM19leGVyY2lzZXMuUm1kKQ0KDQojIyBDaGFsbGVuZ2UgcHJvYmxlbSEgDQoNClRoaXMgcHJvYmxlbSB1c2VzIHRoZSBkYXRhIGZyb20gdGhlIFRpZHkgVHVlc2RheSBjb21wZXRpdGlvbiB0aGlzIHdlZWssIGBraWRzYC4gSWYgeW91IG5lZWQgdG8gcmVmcmVzaCB5b3VyIG1lbW9yeSBvbiB0aGUgZGF0YSwgcmVhZCBhYm91dCBpdCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL3Jmb3JkYXRhc2NpZW5jZS90aWR5dHVlc2RheS9ibG9iL21hc3Rlci9kYXRhLzIwMjAvMjAyMC0wOS0xNS9yZWFkbWUubWQpLiANCg0KICAyMS4gSW4gdGhpcyBleGVyY2lzZSwgeW91IGFyZSBnb2luZyB0byB0cnkgdG8gcmVwbGljYXRlIHRoZSBncmFwaCBiZWxvdywgY3JlYXRlZCBieSBHZW9yZ2lvcyBLYXJhbWFuaXMuIEknbSBzdXJlIHlvdSBjYW4gZmluZCB0aGUgZXhhY3QgY29kZSBvbiBHaXRIdWIgc29tZXdoZXJlLCBidXQgKipET04nVCBETyBUSEFUISoqIFlvdSB3aWxsIG9ubHkgYmUgZ3JhZGVkIGZvciBwdXR0aW5nIGFuIGVmZm9ydCBpbnRvIHRoaXMgcHJvYmxlbS4gU28sIGdpdmUgaXQgYSB0cnkgYW5kIHNlZSBob3cgZmFyIHlvdSBjYW4gZ2V0IHdpdGhvdXQgZG9pbmcgdG9vIG11Y2ggZ29vZ2xpbmcuIEhJTlQ6IHVzZSBgZmFjZXRfZ2VvKClgLiBUaGUgZ3JhcGhpYyB3b24ndCBsb2FkIGJlbG93IHNpbmNlIGl0IGNhbWUgZnJvbSBhIGxvY2F0aW9uIG9uIG15IGNvbXB1dGVyLiBTbywgeW91J2xsIGhhdmUgdG8gcmVmZXJlbmNlIHRoZSBvcmlnaW5hbCBodG1sIG9uIHRoZSBtb29kbGUgcGFnZSB0byBzZWUgaXQuDQogIA0KICANCmBgYHtyIGZpZy53aWR0aD0xMSwgZmlnLmhlaWdodD05fQ0Ka2lkcyAlPiUNCiAgZmlsdGVyKHZhcmlhYmxlICVpbiUgImxpYiIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gaW5mX2Fkal9wZXJjaGlsZCkpICsNCiAgZ2VvbV9saW5lKGNvbG9yID0gIndoaXRlIiwgc2l6ZSA9MikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiIikgKw0KICB0aGVtZV92b2lkKCkgKw0KICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJsaWdodHN0ZWVsYmx1ZTQiKSkgKw0KICBmYWNldF9nZW8odmFycyhzdGF0ZSksIGdyaWQgPSAidXNfc3RhdGVfZ3JpZDMiLCBsYWJlbCA9ICJuYW1lIikgKw0KICBsYWJzKHRpdGxlPSJDaGFuZ2UgaW4gcHVibGljIHNwZW5kaW5nIG9uIGxpYnJhcmllcyIsDQogICAgICAgc3VidGl0bGUgPSAiRG9sbGFycyBzcGVudCBwZXIgY2hpbGQsIGFkanVzdGVkIGZvciBpbmZsYXRpb24iKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9MjAsIGZhY2UgPSAiYm9sZCIpLA0KICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBzaXplID0gMTUpKQ0KICANCmBgYA0KDQoNCg0KKipESUQgWU9VIFJFTUVNQkVSIFRPIFVOQ09NTUVOVCBUSEUgT1BUSU9OUyBBVCBUSEUgVE9QPyoqDQo=